﻿using FreeSql;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using static FreeSql.Internal.GlobalFilter;

namespace FreesqlSimpleExtension;

public static class FreesqlExtensions
{
    /// <summary>
    /// 设置实体属性
    /// </summary>
    /// <typeparam name="TEntity">实体模型</typeparam>
    /// <param name="entity">待更新实体</param>
    /// <param name="updateModel">待更新模型</param>
    /// <param name="isUpdateNull">是否更新空值</param>
    /// <param name="ignoreFields">忽略的字段</param>
    /// <created>tanch,2020/7/15</created>
    /// <changed>tanch,2020/7/15</changed>
    public static void ModifyEntity<TEntity>(this TEntity entity, object updateModel, bool isUpdateNull = true, params string[] ignoreFields)
    {
        //取出更新模型的属性
        var porps = updateModel.GetType().GetProperties();
        //取出实体的属性
        var entifyProps = typeof(TEntity).GetProperties();
        foreach (var item in porps)
        {
            if (ignoreFields.Contains(item.Name))
            {
                continue;
            }
            //取出更新模型对应属性的值
            var value = item.GetValue(updateModel);
            //取出对应实体的属性
            var entifyProp = entifyProps.FirstOrDefault(a => a.Name.Equals(item.Name));
            //空值判断
            if (!isUpdateNull && (value == null || entifyProp == null))
                continue;
            if (!isUpdateNull && item.PropertyType.Equals(typeof(string)) && string.IsNullOrEmpty(value as string))
                continue;
            //把更新模型属性的值设置到实体上
            entifyProp?.SetValue(entity, value);
        }
    }

    /// <summary>
    /// 设置实体属性
    /// </summary>
    /// <typeparam name="TEntity">实体模型</typeparam>
    /// <param name="entity">待更新实体</param>
    /// <param name="updateModel">待更新模型</param>
    /// <param name="isUpdateNull">是否更新空值</param>
    /// <param name="props">只更新的字段</param>
    /// <created>tanch,2020/7/15</created>
    /// <changed>tanch,2020/7/15</changed>
    public static void ModifyEntityProps<TEntity>(this TEntity entity, object updateModel, bool isUpdateNull = true, params string[] props)
    {
        //取出更新模型的属性
        var porps = updateModel.GetType().GetProperties();
        //取出实体的属性
        var entifyProps = typeof(TEntity).GetProperties();
        foreach (var item in porps)
        {
            if (!props.Contains(item.Name))
            {
                continue;
            }
            //取出更新模型对应属性的值
            var value = item.GetValue(updateModel);
            //取出对应实体的属性
            var entifyProp = entifyProps.FirstOrDefault(a => a.Name.Equals(item.Name));
            //空值判断
            if (!isUpdateNull && (value == null || entifyProp == null))
                continue;
            if (!isUpdateNull && item.PropertyType.Equals(typeof(string)) && string.IsNullOrEmpty(value as string))
                continue;
            //把更新模型属性的值设置到实体上
            entifyProp?.SetValue(entity, value);
        }
    }

    /// <summary>
    /// 设置实体属性
    /// </summary>
    /// <typeparam name="TEntity">实体模型</typeparam>
    /// <param name="entity">待更新实体</param>
    /// <param name="updateModel">待更新模型</param>
    /// <created>tanch,2020/7/15</created>
    /// <changed>tanch,2020/7/15</changed>
    public static void ModifyEntity<TEntity>(this TEntity entity, Dictionary<string, object> updateModel)
    {
        //取出实体的属性
        var entifyProps = typeof(TEntity).GetProperties();
        foreach (var item in updateModel)
        {
            //取出更新模型对应属性的值
            var value = item.Value;
            //取出对应实体的属性
            var entifyProp = entifyProps.FirstOrDefault(a => a.Name.Equals(item.Key));
            if (entifyProp == null)
            {
                continue;
            }
            try
            {
                if (entifyProp.PropertyType.IsArrayOrList())
                {
                    if (value is JToken)
                    {
                        var arrValue = value as JToken;
                        //获取目标集合类型
                        var specificListType = typeof(List<>).MakeGenericType(entifyProp.PropertyType.GenericTypeArguments[0]);

                        // 获取 JToken 的 ToObject<T>() 方法
                        MethodInfo toObjectMethod = typeof(JToken).GetMethod("ToObject", Type.EmptyTypes);
                        MethodInfo toObjectGenericMethod = toObjectMethod.MakeGenericMethod(specificListType);

                        // 调用 ToObject<T>() 转换成目标集合类型方法
                        object result = toObjectGenericMethod.Invoke(arrValue, null);
                        entifyProp?.SetValue(entity, result);
                    }
                    continue;
                }
                //可空类型处理
                else if (entifyProp.PropertyType.IsGenericType && entifyProp.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    if (value == null)
                    {
                        entifyProp?.SetValue(entity, null);
                    }
                    else
                    {
                        entifyProp?.SetValue(entity, Convert.ChangeType(value, entifyProp.PropertyType.GetGenericArguments()[0]));
                    }
                }
                else
                {
                    entifyProp?.SetValue(entity, Convert.ChangeType(value, entifyProp.PropertyType));
                }
            }
            catch
            {
                Console.WriteLine($"[{entifyProp.Name}]设置值异常");
                throw;
            }
        }
    }
    /// <summary>
    /// 实体查询
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="updateModel">待更新模型</param>
    /// <returns></returns>
    public static ISelect<TSource> QueryEntity<TSource>(this ISelect<TSource> source, object updateModel)
        where TSource : class, new()
    {
        var props = updateModel.GetType().GetProperties();
        var entifyProps = typeof(TSource).GetProperties();
        var tableExp = Expression.Parameter(typeof(TSource), "t");  //实体对象t=>

        foreach (var item in props)
        {
            var inputVal = item.GetValue(updateModel);
            if (inputVal == null || (inputVal is string v && string.IsNullOrEmpty(v)))
            {
                continue;
            }
            var fsAttr = item.GetCustomAttribute<FsAttribute>();
            if (fsAttr == null)
            {
                continue;
            }

            //跳过排序字段
            if (fsAttr is FsOrderByAttribute)
            {
                continue;
            }

            var entityName = string.IsNullOrEmpty(fsAttr.Name) ? item.Name : fsAttr.Name;
            var entifyProp = entifyProps.FirstOrDefault(a => a.Name == entityName);
            if (entifyProp == null)
            {
                continue;
            }

            //最终lambda表达式
            Expression<Func<TSource, bool>> lambda = null;
            //拼接表达式 左边值 t=>t.key
            var tableMember = Expression.Property(tableExp, entityName);
            //取得 item 值,并转换成左侧类型的值
            ConstantExpression constantExp = null;

            if (fsAttr is FsGteAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.GreaterThanOrEqual(tableMember, constantExp);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //大于
            else if (fsAttr is FsGtAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.GreaterThan(tableMember, constantExp);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //模糊查询
            else if (fsAttr is FsContainsAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var attr = fsAttr as FsContainsAttribute;
                var methodInfo = tableMember.Type.GetMethod("Contains", [tableMember.Type]);
                Expression baseExp = Expression.Constant(false);
                if (attr.ContainsArray.Count != 0)
                {
                    attr.ContainsArray.ForEach(item => {
                        var member = Expression.Property(tableExp, item);
                        var exp = Expression.Call(member, methodInfo, constantExp);
                        baseExp = attr.IsAnd ? Expression.AndAlso(baseExp, exp) : Expression.OrElse(baseExp, exp);
                    });
                }
                else
                {
                    baseExp = Expression.Call(tableMember, methodInfo, constantExp);
                }

                lambda = Expression.Lambda<Func<TSource, bool>>(baseExp, tableExp);
            }
            //包含
            else if (fsAttr is FsArrayContainsAttribute)
            {
                constantExp = Expression.Constant(inputVal, inputVal.GetType());
                MethodCallExpression containsExp = Expression.Call(constantExp, constantExp.Type.GetMethod("Contains", [tableMember.Type]), tableMember);
                lambda = Expression.Lambda<Func<TSource, bool>>(containsExp, tableExp);
            }
            //小于
            else if (fsAttr is FsLtAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.LessThan(tableMember, constantExp);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //小于等于
            else if (fsAttr is FsLteAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.LessThanOrEqual(tableMember, constantExp);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //等于
            else if (fsAttr is FsEqAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.Equal(tableMember, constantExp);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //日期等于
            else if (fsAttr is FsDateEqAttribute)
            {
                constantExp = Expression.Constant(inputVal, tableMember.Type);
                var result = Expression.Equal(Expression.Property(tableMember, "Date"), Expression.Property(constantExp, "Date"));
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            //空值判断
            else if (fsAttr is FsNullAttribute)
            {
                var attr = fsAttr as FsNullAttribute;
                var tableMemberType = tableMember.Type;
                // 对于字符串类型，需要检查 null 和空字符串
                if (tableMemberType == typeof(string))
                {
                    // 检查是否为 null
                    var nullCheck = Expression.Equal(tableMember, Expression.Constant(null, typeof(string)));

                    if (attr.CheckEmptyString)
                    {
                        // 检查是否为空字符串
                        var emptyCheck = Expression.Equal(tableMember, Expression.Constant(string.Empty));
                        // 组合：null OR 空字符串
                        var orExpression = Expression.OrElse(nullCheck, emptyCheck);
                        lambda = Expression.Lambda<Func<TSource, bool>>(orExpression, tableExp);
                    }
                    else 
                    {
                        lambda = Expression.Lambda<Func<TSource, bool>>(nullCheck, tableExp);
                    }
                }
                // 对于可空值类型
                else if (tableMemberType.IsGenericType && tableMemberType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // 检查 HasValue 是否为 false
                    var hasValueProperty = Expression.Property(tableMember, "HasValue");
                    var notHasValue = Expression.Not(hasValueProperty);
                    lambda = Expression.Lambda<Func<TSource, bool>>(notHasValue, tableExp);
                }
                // 对于引用类型（非字符串）
                else if (!tableMemberType.IsValueType)
                {
                    var nullCheck = Expression.Equal(tableMember, Expression.Constant(null, tableMemberType));
                    lambda = Expression.Lambda<Func<TSource, bool>>(nullCheck, tableExp);
                }
                else
                {
                    throw new InvalidOperationException("无效的类型");
                }
            }
            //非空值判断
            else if (fsAttr is FsNotNullAttribute)
            {
                var attr = fsAttr as FsNotNullAttribute;
                var tableMemberType = tableMember.Type;
                // 对于字符串类型，需要检查 null 和空字符串
                if (tableMemberType == typeof(string))
                {
                    // 检查是否为 null
                    var nullCheck = Expression.NotEqual(tableMember, Expression.Constant(null, typeof(string)));

                    if (attr.CheckEmptyString)
                    {
                        // 检查是否为空字符串
                        var emptyCheck = Expression.NotEqual(tableMember, Expression.Constant(string.Empty));
                        // 组合：null OR 空字符串
                        var andExpression = Expression.AndAlso(nullCheck, emptyCheck);
                        lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, tableExp);
                    }
                    else
                    {
                        lambda = Expression.Lambda<Func<TSource, bool>>(nullCheck, tableExp);
                    }
                }
                // 对于可空值类型
                else if (tableMemberType.IsGenericType && tableMemberType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // 检查 HasValue 是否为 false
                    var hasValueProperty = Expression.Property(tableMember, "HasValue");
                    lambda = Expression.Lambda<Func<TSource, bool>>(hasValueProperty, tableExp);
                }
                // 对于引用类型（非字符串）
                else if (!tableMemberType.IsValueType)
                {
                    var nullCheck = Expression.NotEqual(tableMember, Expression.Constant(null, tableMemberType));
                    lambda = Expression.Lambda<Func<TSource, bool>>(nullCheck, tableExp);
                }
                else
                {
                    throw new InvalidOperationException("无效的类型");
                }
            }
            //范围判断
            else if (fsAttr is FsRangeAttribute)
            {
                var attr = fsAttr as FsRangeAttribute;
                
                var (obj1, obj2) = GetFirstTwoFromEnumerable(inputVal);
                if (obj1==null || obj2==null)
                {
                    throw new InvalidOperationException("输入参数错误或者数量小于2");
                }
                var minValue = Expression.Constant(obj1, tableMember.Type);
                var maxValue = Expression.Constant(obj2, tableMember.Type);
                BinaryExpression minCheck = null;
                BinaryExpression maxCheck = null;
                switch (attr.Type)
                {
                    case "[]":
                        minCheck = Expression.GreaterThanOrEqual(tableMember, minValue);
                        maxCheck = Expression.LessThanOrEqual(tableMember, maxValue);
                        break;
                    case "[)":
                        minCheck = Expression.GreaterThanOrEqual(tableMember, minValue);
                        maxCheck = Expression.LessThan(tableMember, maxValue);
                        break;
                    case "()":
                        minCheck = Expression.GreaterThan(tableMember, minValue);
                        maxCheck = Expression.LessThan(tableMember, maxValue);
                        break;
                    case "(]":
                        minCheck = Expression.GreaterThan(tableMember, minValue);
                        maxCheck = Expression.LessThanOrEqual(tableMember, maxValue);
                        break;
                    default:
                        throw new InvalidOperationException("type参数类型错误");
                }
                var result = Expression.AndAlso(minCheck, maxCheck);
                lambda = Expression.Lambda<Func<TSource, bool>>(result, tableExp);
            }
            else
            {
                continue;
            }
            source = source.Where(lambda);
        }

        //排序处理
        var sortProp = props.FirstOrDefault(a => a.GetCustomAttribute<FsOrderByAttribute>() != null);
        if (sortProp != null)
        {
            if (sortProp.GetValue(updateModel) is not List<string> sortConfigs)
            {
                return source;
            }
            foreach (var item in sortConfigs)
            {
                var text = item.Trim();
                var items = new[] { text };
                if (text.Contains(' '))
                {
                    items = item.Split(' ');
                }
                else if(text.Contains(','))
                {
                    items = item.Split(',');
                }
                source = source.OrderByPropertyName(items[0], items.Length <= 1 || items[1] != "desc");
            }
        }

        return source;
    }

    public static (object first, object second) GetFirstTwoFromEnumerable(object enumerableObject)
    {
        if (enumerableObject == null)
            return (null, null);

        if (enumerableObject is IEnumerable enumerable)
        {
            var enumerator = enumerable.GetEnumerator();
            object first = null;
            object second = null;

            if (enumerator.MoveNext())
            {
                first = enumerator.Current;

                if (enumerator.MoveNext())
                {
                    second = enumerator.Current;
                }
            }
            return (first, second);
        }

        return (null, null);
    }
}
